﻿
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using KalevaAalto.DataPart.Excel.Models;
using KalevaAalto.DataPart.Excel.Epplus;
using System.Collections;
using NetTaste;
using KalevaAalto.DataPart.Models;
using System.CodeDom;
using KalevaAalto.DataPart.Enums;
using System.Reflection;
using KalevaAalto.TypePart;
using System.Collections.Immutable;

namespace KalevaAalto.DataPart.Excel.Base
{
    public abstract class BaseRange :BaseWorksheetElement, IEnumerable<BaseRange?>
    {
        public BaseRange(BaseWorksheet worksheet):base(worksheet) 
        {
            
        }


        /// <summary>
        /// 是否合并单元格
        /// </summary>
        public abstract bool Merge { get; set; }

        /// <summary>
        /// 单元格样式
        /// </summary>
        public abstract IStyle Style { get; }

        /// <summary>
        /// 单元格所在行
        /// </summary>
        public abstract BaseRows Rows { get; }

        /// <summary>
        /// 单元格所在列
        /// </summary>
        public abstract BaseColumns Columns { get; }

        /// <summary>
        /// 起始坐格
        /// </summary>
        public CellPos StartPos => Pos.StartPos;

        /// <summary>
        /// 结束坐标
        /// </summary>
        public CellPos EndPos => Pos.EndPos;


        /// <summary>
        /// 单元格区域位置
        /// </summary>
        public abstract RangePos Pos { get; }


        public abstract BaseRange? this[CellPos cellPos] { get; }
        public BaseRange? this[int row, int column] => this[new CellPos(row, column)];

        public abstract BaseRange? this[RangePos rangePos] { get; }
        public BaseRange? this[int startRow, int startColumn, int endRow, int endColumn] => this[new RangePos(startRow, startColumn, endRow, endColumn)];

        public abstract void SetTable(string tableName = EmptyString);



        /// <summary>
        /// 清空单元格
        /// </summary>
        public void Clear()
        {
            foreach(var range in this)
            {
                if(range is not null)
                {
                    range.Value = null;
                }
            }
        }


        public virtual DataTable GetDataTable(string tableName = EmptyString)
        {
            DataTable result = new DataTable(tableName);
            int rowCout = Rows.Count, columnCout = Columns.Count;
            IDictionary<short, int> columnIndexs = new Dictionary<short, int>();
            short dataColumnCount = 0;
            for (int column = 1; column <= columnCout; column++)
            {
                string columnName = GetNewColumnName(this[1, column].Value?.ToString(),result.Columns.Cast<DataColumn>().Select(it=>it.ColumnName));
                if (!string.IsNullOrEmpty(columnName))
                {
                    result.Columns.Add(columnName, typeof(object));
                    columnIndexs.Add(dataColumnCount, column);
                    dataColumnCount++;
                }
            }

            for (int row = 2; row <= rowCout; row++)
            {
                DataRow dataRow = result.Rows.Add();
                for (short j = 0; j < dataColumnCount; j++)
                {
                    object? value = this[row, columnIndexs[j]]!.Value;
                    if (value is null) dataRow[j] = DBNull.Value;
                    else dataRow[j] = value;
                }
            }

            return result;
        }









        #region 获取数据表



        
        private static IDictionary<string,int> GetColumnIndexs(object?[][] objss,int rowCount,int columnCount)
        {
            IDictionary<string, int> result = new Dictionary<string, int>();
            for (int column = 0; column < columnCount; column++)
            {

                try
                {
                    string columnName = GetNewColumnName(objss[0][column]?.ToString(), result.Keys);
                    if (!string.IsNullOrEmpty(columnName))
                    {
                        result.Add(columnName, column);
                    }
                }
                catch(Exception e)
                {
                    throw new Exception($"录入第{column}个字段时出错：{e.Message}");
                }

                
            }
            return result.ToImmutableDictionary();
        }





        public virtual DataTable GetDataTable(string tableName, DataColumnMatchCollection dataColumnMatches)
        {
            var rowCount = Rows.Count;
            var columnCount = Columns.Count;
            var objss = Values;

            //获取字段列表
            IDictionary<string, int> columnIndexs = GetColumnIndexs(objss, rowCount, columnCount);


            //新建数据表
            DataTable result = dataColumnMatches.GenerateNewDataTable(tableName);
            IDictionary<string, string> matches = new Dictionary<string, string>();
            ICollection<string> sourceColumnNames = columnIndexs.Keys.ToHashSet();
            foreach (var item in dataColumnMatches)
            {
                foreach (var sourceColumnName in item.SourceColumnNames)
                {
                    if (sourceColumnNames.Contains(sourceColumnName))
                    {
                        matches.Add(item.ColumnName, sourceColumnName);
                        sourceColumnNames.Remove(sourceColumnName);
                        break;
                    }

                }
                if (!matches.ContainsKey(item.ColumnName))
                {
                    throw new Exception($"找不到字段“{item.ColumnName}”;");
                }
            }


            




            for (int row = 1; row < rowCount; row++)
            {
                DataRow dataRow = result.Rows.Add();
                foreach(var item in dataColumnMatches)
                {
                    var value = objss[row][columnIndexs[matches[item.ColumnName]]];
                    if (value is null)
                    {
                        dataRow[item.ColumnName] = DBNull.Value;
                    }
                    else if(item.Type == typeof(DateTime))
                    {
                        DateTime dateTime = DateTime.FromOADate((double)value);
                        dataRow[item.ColumnName] = dateTime;
                    }
                    else
                    {
                        dataRow[item.ColumnName] = value;
                    }
                }
            }

            return result;
        }



        public virtual T[] GetArray<T>(DataColumnMatchCollection dataColumnMatches) where T : class ,new()
        {
            var rowCount = Rows.Count;
            var columnCount = Columns.Count;
            var objss = Values;

            //获取字段列表
            IDictionary<string, int> columnIndexs = GetColumnIndexs(objss, rowCount, columnCount);





            //匹配字段
            List<T> objs = new List<T>();
            IDictionary<string, string> matches = new Dictionary<string, string>();
            ICollection<string> sourceColumnNames = columnIndexs.Keys.ToHashSet();
            IDictionary<string, PropertyInfo> properties = GetColumnInfoDic(typeof(T), false);
            foreach (var item in dataColumnMatches)
            {
                foreach (var sourceColumnName in item.SourceColumnNames)
                {
                    if (sourceColumnNames.Contains(sourceColumnName))
                    {
                        matches.Add(item.ColumnName, sourceColumnName);
                        sourceColumnNames.Remove(sourceColumnName);
                        break;
                    }

                }
                if (!matches.ContainsKey(item.ColumnName))
                {
                    throw new Exception($"找不到字段“{item.ColumnName}”;");
                }
                if (!properties.ContainsKey(item.ColumnName))
                {
                    throw new Exception($"实体中不存在字段“{item.ColumnName}”;");
                }
            }




            for (int row = 1; row < rowCount; row++)
            {
                T obj = new T();
                foreach (var item in dataColumnMatches)
                {
                    var valueSource = objss[row][columnIndexs[matches[item.ColumnName]]];
                    PropertyInfo propertyInfo = properties[item.ColumnName];
                    if (valueSource is not null && propertyInfo.PropertyType == typeof(DateTime))
                    {
                        DateTime dateTime = DateTime.FromOADate((double)typeof(double).GetValue(valueSource)!);
                        propertyInfo.SetValue(obj, dateTime);
                    }
                    object? value = propertyInfo.PropertyType.GetValue(valueSource);
                    propertyInfo.SetValue(obj, value);

                }
                objs.Add(obj);
            }

            return objs.ToArray();
        }


        #endregion


        public virtual IEnumerator<BaseRange?> GetEnumerator()
        {
            var rowCount = Rows.Count;
            var columnCount = Columns.Count;
            var maxCount = System.Math.Max(rowCount,columnCount);

            for(int i = 1; i <= maxCount; i++)
            {
                for(int j = 1; j< i;j++)
                {
                    if( i <= rowCount && j <= columnCount)
                    {
                        yield return this[i, j];
                    }
                    if( j <= rowCount && i <= columnCount)
                    {
                        yield return this[j, i];
                    }
                }

                if (i <= rowCount && i <= columnCount)
                {
                    yield return this[i, i];
                }

            }
        }

        IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();



        public virtual BaseRange? Search(Func<BaseRange,bool> func)
        {
            foreach(var cell in this)
            {
                if(cell is not null && func(cell) == true)
                {
                    return cell;
                }
            }

            return null;
        }





        public abstract object? Value { get; set; }

        public bool IsError => ErrorType != ErrorType.None;

        public abstract ErrorType ErrorType { get; }


        /// <summary>
        /// 批量设置单元格的值
        /// </summary>
        /// <param name="valuess">单元格的值</param>
        public virtual void SetValue(object?[]?[] valuess)
        {
            //清空当前单元格中的内容
            Clear();


            if (valuess is null)
            {
                return;
            }

            var valuessRowCount = valuess.Length;
            if (valuessRowCount <= 0)
            {
                return;
            }
            

            var rowCount = Rows.Count;
            var columnCount = Columns.Count;
            if (rowCount <= 0 || columnCount <= 0)
            {
                return;
            }

            for(int row = 0;row<valuessRowCount && row < rowCount; row++)
            {
                var values = valuess[row];
                if (values is null)
                {
                    continue;
                }
                int valuessColumnCount = values.Length;
                for (int column = 0;column<valuessColumnCount && column < columnCount; column++)
                {
                    var cell = this[row + 1, column + 1];
                    if (cell is not null)
                    {
                        var value = values[column];
                        cell.Value = value;
                    }
                }
            }
        }

        
        public virtual object?[][] Values
        {
            get
            {
                var objss = new List<object?[]>();
                var rowCout = Rows.Count;
                var columnCout = Columns.Count;
                for (int row = 1; row <= rowCout; row++)
                {
                    List<object?> objs = new List<object?>();
                    for (int column = 1; column <= columnCout; column++)
                    {
                        var cell = this[row, column];
                        objs.Add(cell?.Value ?? null);
                    }
                    objss.Add(objs.ToArray());
                }
                return objss.ToArray();
            }
        }

    }
}
